package com.utils.redis;
import java.util.HashSet;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.utils.StringUtils;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Redis普遍属性
 * @author: dingjiejun
 * @version: 1.0
 * @date: 2018/4/10
 */
public abstract class BaseRedisSupport {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    protected JedisPool jedisPool;

    protected JedisCluster jedisCluster;

    /**
     * 是否集群
     */
    protected boolean cluster = false;

    /**
     * 地址+端口：多个地址逗号分隔
     */
    protected String address;

    /**
     * redis密码
     */
    protected String password;

    /**
     * 命令超时时间（ms）
     */
    protected int timeout = 6000;

    /**
     * 重试次数
     */
    protected int maxAttempts = 3;

    /**
     * 最大连接数
     */
    protected int maxTotal = 150;

    /**
     * 最大空闲连接数
     */
    protected int maxIdle = 10;

    /**
     * 初始化连接数
     */
    protected int minIdle = 2;

    /**
     * 获取连接时的最大等待毫秒数
     */
    protected int maxWaitMillis = 5000;

    /**
     * 创建时验证
     */
    protected boolean testOnCreate = false;

    /**
     * 获取连接时验证
     */
    protected boolean testOnBorrow = true;

    /**
     * 归还连接时验证
     */
    protected boolean testOnReturn = true;

    /**
     * 空闲时验证
     */
    protected boolean testWhileIdle = true;

    /**
     * 两次验证间隔
     */
    protected long timeBetweenEvictionRunsMillis = 30000;

    /**
     * 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
     */
    protected int numTestsPerEvictionRun = 10;

    /**
     * 逐出连接的最小空闲时间
     */
    protected long minEvictableIdleTimeMillis = 30000;

    /**
     * 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略)
     */
    protected long softMinEvictableIdleTimeMillis = 1800000;

    public boolean isCluster() {
        return cluster;
    }

    public void setCluster(boolean cluster) {
        this.cluster = cluster;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public int getMaxAttempts() {
        return maxAttempts;
    }

    public void setMaxAttempts(int maxAttempts) {
        this.maxAttempts = maxAttempts;
    }

    public int getMaxTotal() {
        return maxTotal;
    }

    public void setMaxTotal(int maxTotal) {
        this.maxTotal = maxTotal;
    }

    public int getMaxIdle() {
        return maxIdle;
    }

    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }

    public int getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public int getMaxWaitMillis() {
        return maxWaitMillis;
    }

    public void setMaxWaitMillis(int maxWaitMillis) {
        this.maxWaitMillis = maxWaitMillis;
    }

    public boolean isTestOnCreate() {
        return testOnCreate;
    }

    public void setTestOnCreate(boolean testOnCreate) {
        this.testOnCreate = testOnCreate;
    }

    public boolean isTestOnBorrow() {
        return testOnBorrow;
    }

    public void setTestOnBorrow(boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    public boolean isTestOnReturn() {
        return testOnReturn;
    }

    public void setTestOnReturn(boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    public boolean isTestWhileIdle() {
        return testWhileIdle;
    }

    public void setTestWhileIdle(boolean testWhileIdle) {
        this.testWhileIdle = testWhileIdle;
    }

    public long getTimeBetweenEvictionRunsMillis() {
        return timeBetweenEvictionRunsMillis;
    }

    public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    }

    public int getNumTestsPerEvictionRun() {
        return numTestsPerEvictionRun;
    }

    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
        this.numTestsPerEvictionRun = numTestsPerEvictionRun;
    }

    public long getMinEvictableIdleTimeMillis() {
        return minEvictableIdleTimeMillis;
    }

    public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
    }

    public long getSoftMinEvictableIdleTimeMillis() {
        return softMinEvictableIdleTimeMillis;
    }

    public void setSoftMinEvictableIdleTimeMillis(long softMinEvictableIdleTimeMillis) {
        this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis;
    }

    protected void createConnection(){

        if(StringUtils.isBlank(address)){
            return;
        }

        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 设置最大连接数
        jedisPoolConfig.setMaxTotal(maxTotal);

        // 设置最大空闲连接数
        jedisPoolConfig.setMaxIdle(maxIdle);

        // 设置最小空闲连接数或者说初始化连接数
        jedisPoolConfig.setMinIdle(minIdle);

        // 设置最大等待时间
        jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);

        //获取时验证
        jedisPoolConfig.setTestOnBorrow(true);

        //归还时验证
        jedisPoolConfig.setTestOnReturn(true);

        //空闲时验证
        jedisPoolConfig.setTestWhileIdle(true);

        //两次验证间隔
        jedisPoolConfig.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

        //最大空闲停留时间
        jedisPoolConfig.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);

        //每次验证数
        jedisPoolConfig.setNumTestsPerEvictionRun(numTestsPerEvictionRun);

        String[] hostAndPorts = address.split(",");
        if (cluster) {
            Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
            for (String hostAndPortStr : hostAndPorts) {
                jedisClusterNodes.add(HostAndPort.parseString(hostAndPortStr));
            }
            if (StringUtils.isNotBlank(password)) {
                jedisCluster = new JedisCluster(jedisClusterNodes, timeout, timeout, maxAttempts, password, jedisPoolConfig);
            } else {
                jedisCluster = new JedisCluster(jedisClusterNodes, timeout, maxAttempts, jedisPoolConfig);
            }

        } else {
            HostAndPort hostAndPort = HostAndPort.parseString(hostAndPorts[0]);
            if (StringUtils.isNotBlank(password)) {
                jedisPool = new JedisPool(jedisPoolConfig, hostAndPort.getHost(), hostAndPort.getPort(), timeout, password);
            } else {
                jedisPool = new JedisPool(jedisPoolConfig, hostAndPort.getHost(), hostAndPort.getPort(), timeout);
            }
        }
    }

    /**
     * 同步获取Jedis实例
     *
     * @return Jedis
     */
    public Jedis getJedis() {
        if (jedisPool == null) {
            throw new RuntimeException("redis未初始化");
        }
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
        } catch (Exception e) {
            logger.error("Get jedis error : " + e);
        }
        return jedis;
    }

    public void returnClient(final Jedis jedis) {
        if (jedis != null) {
            //returnResource方法已过期，close会自动判定是否需要回收
            jedis.close();
        }
    }

}
